/******************************************************************************* * Copyright (c) 2000, 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.corext.template.java; import java.util.Arrays; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; /** * Utilities for Signature operations. * * @see Signature * @since 3.1 */ public final class SignatureUtil { /** * The signature of the null type. This type does not really exist in the * type system. It represents the bound of type variables that have no lower * bound, for example the parameter type to the <code>add</code> method of * an instance of <code>java.util.List<? extends Number></code>. * <p> * The only possible value that has that type is <code>null</code>. * </p> * <p> * The representation of the null type is the signature of a type variable * named <code>null</code> ({@value}), which will only work if no such * variable gets declared in the same context. */ private static final String NULL_TYPE_SIGNATURE= "Tnull;"; //$NON-NLS-1$ private static final char[] NULL_TYPE_SIGNATURE_ARRAY= NULL_TYPE_SIGNATURE.toCharArray(); /** * The signature of <code>java.lang.Object</code> ({@value}). */ private static final String OBJECT_SIGNATURE= "Ljava.lang.Object;"; //$NON-NLS-1$ private static final char[] OBJECT_SIGNATURE_ARRAY= OBJECT_SIGNATURE.toCharArray(); private SignatureUtil() { // do not instantiate } /** * Returns <code>true</code> if <code>signature</code> is the * signature of the <code>java.lang.Object</code> type. * * @param signature the signature * @return <code>true</code> if <code>signature</code> is the * signature of the <code>java.lang.Object</code> type, * <code>false</code> otherwise */ public static boolean isJavaLangObject(String signature) { return OBJECT_SIGNATURE.equals(signature); } /** * Returns the upper bound of a type signature. Returns the signature of <code>java.lang.Object</code> if * <code>signature</code> is a lower bound (<code>? super T</code>); returns * the signature of the type <code>T</code> of an upper bound (<code>? extends T</code>) * or <code>signature</code> itself if it is not a bound signature. * * @param signature the signature * @return the upper bound signature of <code>signature</code> */ public static String getUpperBound(String signature) { return String.valueOf(getUpperBound(signature.toCharArray())); } /** * Returns the upper bound of a type signature. Returns the signature of <code>java.lang.Object</code> if * <code>signature</code> is a lower bound (<code>? super T</code>); returns * the signature of the type <code>T</code> of an upper bound (<code>? extends T</code>) * or <code>signature</code> itself if it is not a bound signature. * * @param signature the signature * @return the upper bound signature of <code>signature</code> */ public static char[] getUpperBound(char[] signature) { if (signature.length < 1) return signature; if (signature[0] == Signature.C_STAR) return OBJECT_SIGNATURE_ARRAY; int superIndex= indexOf(signature, Signature.C_SUPER); if (superIndex == 0) return OBJECT_SIGNATURE_ARRAY; if (superIndex != -1) { char afterSuper= signature[superIndex + 1]; if (afterSuper == Signature.C_STAR) { char[] type= new char[signature.length - 1]; System.arraycopy(signature, 0, type, 0, superIndex); type[superIndex]= Signature.C_STAR; System.arraycopy(signature, superIndex + 2, type, superIndex + 1, signature.length - superIndex - 2); return getUpperBound(type); } if (afterSuper == Signature.C_EXTENDS) { int typeEnd= typeEnd(signature, superIndex + 1); char[] type= new char[signature.length - (typeEnd - superIndex - 1)]; System.arraycopy(signature, 0, type, 0, superIndex); type[superIndex]= Signature.C_STAR; System.arraycopy(signature, typeEnd, type, superIndex + 1, signature.length - typeEnd); return getUpperBound(type); } } if (signature[0] == Signature.C_EXTENDS) { char[] type= new char[signature.length - 1]; System.arraycopy(signature, 1, type, 0, signature.length - 1); return type; } return signature; } /** * Returns the lower bound of a type signature. Returns the null type * signature if <code>signature</code> is a wildcard or upper bound (<code>? extends T</code>); * returns the signature of the type <code>T</code> of a lower bound (<code>? super T</code>) * or <code>signature</code> itself if it is not a bound signature. * * @param signature the signature * @return the lower bound signature of <code>signature</code> */ public static String getLowerBound(String signature) { return String.valueOf(getLowerBound(signature.toCharArray())); } /** * Returns the lower bound of a type signature. Returns the null type * signature if <code>signature</code> is a wildcard or upper bound (<code>? extends T</code>); * returns the signature of the type <code>T</code> of a lower bound (<code>? super T</code>) * or <code>signature</code> itself if it is not a bound signature. * * @param signature the signature * @return the lower bound signature of <code>signature</code> */ public static char[] getLowerBound(char[] signature) { if (signature.length < 1) return signature; if (signature.length == 1 && signature[0] == Signature.C_STAR) return signature; int superIndex= indexOf(signature, Signature.C_EXTENDS); if (superIndex == 0) return NULL_TYPE_SIGNATURE_ARRAY; if (superIndex != -1) { char afterSuper= signature[superIndex + 1]; if (afterSuper == Signature.C_STAR || afterSuper == Signature.C_EXTENDS) // impossible captured type return NULL_TYPE_SIGNATURE_ARRAY; } char[][] typeArguments= Signature.getTypeArguments(signature); for (int i= 0; i < typeArguments.length; i++) if (Arrays.equals(typeArguments[i], NULL_TYPE_SIGNATURE_ARRAY)) return NULL_TYPE_SIGNATURE_ARRAY; if (signature[0] == Signature.C_SUPER) { char[] type= new char[signature.length - 1]; System.arraycopy(signature, 1, type, 0, signature.length - 1); return type; } return signature; } private static int indexOf(char[] signature, char ch) { for (int i= 0; i < signature.length; i++) { if (signature[i] == ch) return i; } return -1; } /** * Returns the fully qualified type name of the given signature, with any * type parameters and arrays erased. * * @param signature the signature * @return the fully qualified type name of the signature * @throws IllegalArgumentException if the signature is syntactically incorrect */ public static String stripSignatureToFQN(String signature) throws IllegalArgumentException { signature= Signature.getTypeErasure(signature); signature= Signature.getElementType(signature); return Signature.toString(signature); } /** * Returns the qualified signature corresponding to * <code>signature</code>. * * @param signature the signature to qualify * @param context the type inside which an unqualified type will be * resolved to find the qualifier, or <code>null</code> if no * context is available * @return the qualified signature */ public static String qualifySignature(final String signature, final IType context) { if (context == null) return signature; String qualifier= Signature.getSignatureQualifier(signature); if (qualifier.length() > 0) return signature; String elementType= Signature.getElementType(signature); String erasure= Signature.getTypeErasure(elementType); String simpleName= Signature.getSignatureSimpleName(erasure); String genericSimpleName= Signature.getSignatureSimpleName(elementType); int dim= Signature.getArrayCount(signature); try { String[][] strings= context.resolveType(simpleName); if (strings != null && strings.length > 0) qualifier= strings[0][0]; } catch (JavaModelException e) { // ignore - not found } if (qualifier.length() == 0) return signature; String qualifiedType= Signature.toQualifiedName(new String[] {qualifier, genericSimpleName}); String qualifiedSignature= Signature.createTypeSignature(qualifiedType, true); String newSignature= Signature.createArraySignature(qualifiedSignature, dim); return newSignature; } /** * Takes a method signature * <code>[< typeVariableName : formalTypeDecl >] ( paramTypeSig1* ) retTypeSig</code> * and returns it with any parameter signatures filtered through * <code>getLowerBound</code> and the return type filtered through * <code>getUpperBound</code>. Any preceding formal type variable * declarations are removed. * <p> * TODO this is a temporary workaround for * https://bugs.eclipse.org/bugs/show_bug.cgi?id=83600 * </p> * * @param signature the method signature to convert * @return the signature with no bounded types */ public static char[] unboundedSignature(char[] signature) { if (signature == null || signature.length < 2) return signature; final boolean BUG_83600= true; // XXX the signatures from CompletionRequestor contain a superfluous '+' // before type parameters to parameter types if (BUG_83600) { signature= fix83600(signature); } StringBuffer res= new StringBuffer("("); //$NON-NLS-1$ char[][] parameters= Signature.getParameterTypes(signature); for (int i= 0; i < parameters.length; i++) { char[] param= parameters[i]; res.append(getLowerBound(param)); } res.append(')'); res.append(getUpperBound(Signature.getReturnType(signature))); return res.toString().toCharArray(); } /** * TODO this is a temporary workaround for * https://bugs.eclipse.org/bugs/show_bug.cgi?id=83600 and * https://bugs.eclipse.org/bugs/show_bug.cgi?id=85293 * * @param signature the method signature to convert * @return the fixed signature */ public static char[] fix83600(char[] signature) { if (signature == null || signature.length < 2) return signature; return Signature.removeCapture(signature); } private static int typeEnd(char[] signature, int pos) { int depth= 0; while (pos < signature.length) { switch (signature[pos]) { case Signature.C_GENERIC_START: depth++; break; case Signature.C_GENERIC_END: if (depth == 0) return pos; depth--; break; case Signature.C_SEMICOLON: if (depth == 0) return pos + 1; break; } pos++; } return pos + 1; } }